(function() {
	// function.bind
	Function.prototype.bind = function(scope) {
		var me = this;
		return function() {
			return me.apply(scope, Array.prototype.slice.call(arguments));
		};
	};
	// function.defer
	Function.prototype.defer = function(scope, delay) {
		var args = arguments;
		var me = this;
		var fn = function() {
			me.apply(scope, Array.prototype.slice.call(args,2));
		};
		setTimeout(fn, delay);
	};
	//Array.indexOf
	if(!Array.prototype.indexOf){
		Array.prototype.indexOf = function(v) {
			for(var i=0;i<this.length;i++){
				if(this[i] == v){return i;}
			}
			return -1;
		};
	}
	//String.trim
	if(!String.prototype.trim){
		String.prototype.trim=function(){
			return this.replace('^\s+|\s+$', '');
		};
	}
	if(!String.prototype.replaceAll){
		String.prototype.replaceAll = function(regex,str) {
			return this.replace(new RegExp(regex,'g'), str);
		};
	}
})();

(function() {

	/** isType */
	var isType = function(type) {
		return function(obj) {
			return typeof obj == type;
		};
	};

	var load = function(tag, attrs, callback) {
		var dom = S.dom(tag, attrs);
		dom.onload = function() {
			callback && callback(dom);
		};
		document.body.appendChild(dom);
		return dom;
	};

	var cached = {}, mappings = {};
	
	var mouseWheelEvent=function(fn){
		return function(e) {
			e = e || window.event;
			if(e.wheelDelta){
				fn(event.wheelDelta / 120);
			}else{
				fn(-e.detail/3);
			}
			S.stopEvent(e);
		};
	};
	
	/** Window.S */
	window.S = {
		isString : isType('string'),
		isFunction : isType('function'),
		isObject : isType('object'),
		isArray : function(v) {
			return v instanceof Array;
		},
		guid : function() {
			var id = 0;
			return function() {
				return 'S_GUID_' + ++id;
			};
		}(),
		E : function(id) {
			if (S.isString(id)) { return document.getElementById(id); }
			return id;
		},
		define : function(id, path, fn) {
			mappings[id] = fn;
		},
		require : function(id) {
			var exports = cached[id];
			if (!exports) {
				loadScript(id);
				exports = cached[id] = mappings[id]();
			}
			return exports;
		},
		each : function(arr, fn, filter) {
			if (!S.isFunction(fn)) { return; }
			if (filter && !S.isFunction(filter)) { return; }

			if(arr instanceof Array){
				for (var i=0;i<arr.length;i++) {
					if (!filter || filter(i, arr[i])) {
						if (fn(i, arr[i]) === false) {
							break;
						}
					}
				}
			}else{
				for ( var key in arr) {
					if (!filter || filter(key, arr[key])) {
						if (fn(key, arr[key]) === false) {
							break;
						}
					}
				}
			}
		},
		ns : function(namespace) {

			if (arguments.length < 1) { return; }
			var p = window, pkgs = arguments[0];
			if (arguments[1] != null) {
				p = arguments[0];
				pkgs = arguments[1];
			}
			if (!S.isString(pkgs)) { return; }
			var arrs = pkgs.split('.');
			for ( var i in arrs) {
				if (!p[arrs[i]]) {
					p[arrs[i]] = {};
				}
				p[arrs[i]]._parent = p;
				p[arrs[i]]._name = arrs[i];
				p = p[arrs[i]];
			}
			return p;
		},
		args : function(args,from) {
			if(!from){from=0;}
			return Array.prototype.slice.call(args,from);
		},
		extend : function() {
			if (arguments.length == 0) { return; }
			if (S.isString(arguments[0])) {
				if (arguments[1] == null) { return; }

				var name = arguments[0];
				var pkg = this;
				if (name.indexOf('.') > 0) {
					pkg = S.ns(this, name.substring(0, name.lastIndexOf('.')));
					name = name.substring(pkg.length + 1);
				}
				pkg[name] = arguments[1];
			} else {
				var me = this;
				S.each(S.args(arguments), function(i, arg) {
					for ( var key in arg) {
						me[key] = arg[key];
					}
				}, function(i, arg) {
					S.isObject(arg);
				});
			}
		},
		loadCss : function(url, callback) {
			load('link', {
				type : 'text/css',
				rel : 'stylesheet',
				href : url
			}, callback);
		},
		dom : function(tag, attrs) {
			var dom = document.createElement(tag);
			if (attrs) {
				S.each(attrs, function(key, val) {
					dom.setAttribute(key, value);
				});
			}
			return document.createElement(tag);
		},
		addEvent : function(dom, name, listener) {
			//firefox
			if(name == 'mousewheel'  ){
				if( navigator.userAgent.indexOf('Firefox')>0){
					name = 'DOMMouseScroll';
				}
				listener = mouseWheelEvent(listener);
			}
			dom.addEventListener(name, listener, false);
		},
		delEvent : function(dom, name, listener) {
			//firefox
			if(name == 'mousewheel'  ){
				if( navigator.userAgent.indexOf('Firefox')>0){
					name = 'DOMMouseScroll';
				}
				listener = mouseWheelEvent(listener);
			}
			dom.removeEventListener(name, listener, false);
		},
		stopEvent : function(e) {
			e = e || window.event;
			e.stopPropagation && e.stopPropagation();
			e.cancelBubble = true;
			e.returnValue = false;
		},
		stopEventFun : function() {
			return function(e) {
				e = e || window.event;
				e.stopPropagation && e.stopPropagation();
				e.cancelBubble = true;
				e.returnValue = false;
				return false;
			};
		},
		fixConfig : function(conf) {
			
			S.each(S.args(arguments,1), function(i, p) {
				
				for(var k in p){
					if (conf[k] == null) {
						conf[k] = p[k];
					}
				}
			});
			return conf;
		},
		formData : function(frm){
			var $form = $(frm);
			var str =  $form.serialize();
			var result = {};
				
			S.each(str.split('&'),function(i,v){
				var p = v.split('=');
				result[p[0]] = decodeURIComponent(p[1]);
			});
			return result;
		}
	};
	
	S.define('S', function() {
		return S;
	});
})();

S.extend('about', {
	author : 'lion',
	email : 'hl_0074@sina.com',
	version : '1.0',
	date : '2014/09/04'
});

/*获取相对某个父元素的位置*/
S.extend('offset',function(e,p){
	
	p = p || document.body;
	var left = e.offsetLeft;
	var top = e.offsetTop;
	
	while((e = e.offsetParent)!=null && e!=p){
		left += e.offsetLeft;
		top += e.offsetTop;
	}
	return {
		left:left,
		top:top
	};
});

S.extend('ready',function(fn){
	S.addEvent(window, 'load', fn);
});

// require jquery
/**
 * @param String
 *            class name
 * @param Object
 *            instance configs
 * @param Object
 *            Class menbers
 * @return Function class constructor
 */
S.Class = function() {
	var name = arguments[0];
	if (!S.isString(name)) { return; }

	var options = arguments[1] || {};
	var statics = arguments[2];

	var klass = function() {
		if (S.isFunction(options.parent)) {
			options.parent.apply(this, [].slice.apply(arguments));
		}

		// copy primary fields
		var me = this;
		S.each(options, function(k, v) {
			me[k] = v;
		}, function(k, v) {
			return !S.isFunction(v);
		});

		// call init
		if (S.isFunction(options.init)) {
			options.init.apply(this, [].slice.apply(arguments));
		}
	};

	// prototype chains
	var proto = {};
	if (S.isFunction(options.parent)) {
		proto = new options.parent;
		S.each(proto, function(k, v) {
			delete proto[k];
		}, function(k, v) {
			return !S.isFunction(v);
		});
	}
	proto.constructor = klass;
	klass.fn = klass.prototype = proto;
	klass.fn.extend = klass.extend = S.extend;

	// class prototype methods
	S.each(options, function(k, v) {
		klass.fn[k] = v;
	}, function(k, v) {
		return S.isFunction(v);
	});

	// class menbers
	if (S.isObject(statics)) {
		klass.extend(statics);
	}
	S.each(statics, function(k, v) {
		klass[k] = v;
	});

	// namespace
	var ns = window;
	var className = name;
	if (className.indexOf('.') > 0) {
		var pkg = name.substring(0, name.lastIndexOf('.'));
		ns = S.ns(pkg);
		className = name.substring(pkg.length + 1);
	}
	ns[className] = klass;

	return klass;
};
S.Class('S.Router',{
	init:function(){
		this.$win =$(window);
		this.map = new S.Map();
		this.regList = new S.List();
		
		this.enabled = false;
		this.$win.bind('hashchange',function(e){
			if(!this.enabled){return;}
			var hash = window.location.hash.replace('#', '');
			if(hash == ''){hash = 'index';}
			var eve = e.originalEvent;
			if(eve){
				var oldURL = eve.oldURL;
				var oldHash = '';
				if(oldURL.indexOf('#')>-1){
					oldHash = oldURL.substring(oldURL.indexOf('#')+1);
				}
			}
			this.map.each(function(key,fns){
				fns.each(function(i,fn){
					fn && fn(hash,oldHash);
				});
			},function(key,fn){
				if(key == hash ){return true;} 
				return false;
			});
			
			//如果是正则，则需要全部匹配
			this.regList.each(function(i,v) {
				v.fn && v.fn(hash,oldHash);
			},function(i,v){
				var x = hash.match(v.hash);
				if(x && x[0] == hash){return true;}
				return false;
			});
		}.bind(this));
	},
	enable:function(enabled){
		this.enabled = enabled;
	},
	start:function(){
		this.enabled = true;
		this.$win.trigger('hashchange');
	},
	navigate:function(hash){
		if(hash.indexOf('#')!=0){
			hash = '#'+hash;
		}
		this.enabled = true;
		window.location.hash=hash;
	},
	add:function(hash,fn){
		var me = this;
		if(hash instanceof RegExp){
			this.regList.add({hash:hash,fn:fn});
		}else if(S.isObject(hash)){
			S.each(hash, function(k,v) {
				me.add(k, v);
			});
			return;
		}
		
		if(S.isString(hash)){
			if(hash == ''){
				hash = 'index';
			}
			var list = this.map.get(hash);
			if(list == null){
				list = new S.List();
				this.map.put(hash,list);
			}
			list.add(fn);
		}
	},
	remove:function(hash){
		this.map.remove(hash);
		var me = this;
		this.regList.each(function(i,v) {
			if(v.hash == hash){
				me.regList.remove(v);
			}
		});
	}
});
/**
 * @param array
 *            template snippets
 * 
 */

S.Class('S.Map', {
	init : function() {
		this.values = {};
		this._size = 0;
	},
	put : function(key, value) {
		if (this.values[key] != null) {
			this._size++;
		}
		this.values[key] = value;
	},
	remove : function(name) {
		if (this.values[name] != null) {
			this._size--;
		}
		delete this.values[name];
	},
	get : function(key) {
		return this.values[key];
	},
	contains : function(value) {
		var result = false;
		S.each(arr, function(name, value) {
			result = true;
			return false;
		}, function(name, v) {
			return v == value;
		});
		return result;
	},
	clear : function() {
		this.values = {};
		this._size = 0;
	},
	size : function() {
		return this._size;
	},
	each : function(fn, filter) {
		S.each(this.values, fn, filter);
	},
	isEmpty:function(){
		return this.size() == 0;
	}
});
S.Class('S.List', {
	init : function() {
		this.values = [];
	},
	add : function(value) {
		this.values.push(value);
	},
	remove : function(val) {
		var index = this.index(val);
		if(index==-1){return;}
		this.values.splice(index, 1);
	},
	insert : function(i, value) {
		this.values.splice(i, 1, value);
	},
	contains : function(value) {
		var result = false;
		S.each(this.values, function(k, v) {
			result = true;
			return false;
		}, function(k, v) {
			return v == value;
		});
		return result;
	},
	size : function() {
		return this.values.length;
	},
	clear : function() {
		this.values = [];
	},
	each : function(fn, filter) {
		S.each(this.values, fn, filter);
	},
	get : function(index) {
		return this.values[index];
	},
	index : function(value) {
		var result = -1;
		S.each(this.values, function(k, v) {
			result = k;
			return false;
		}, function(k, v) {
			return v == value;
		});
		return result;
	},
	last:function(){
		if(this.size==0){return null;}
		return this.get(this.size()-1);
	},
	first:function(){
		if(this.size==0){return null;}
		return this.get(0);
	},
	isEmpty:function(){
		return this.size() == 0;
	},
	toArray:function(){
		return this.values.slice(0);
	}
});

S.Class('S.Event', {
	init : function() {
		this.events = new S.Map();
	},
	add : function(name, fn) {

		if (!S.isFunction(fn)) { return; }

		var list = this.events.get(name);
		if (list == null) {
			list = new S.List();
			this.events.put(name, list);
		}
		list.add(fn);
	},
	del : function(name, fn) {

		if (!S.isFunction(fn)) {
			this.events.remove(name);
			return;
		}
		var list = this.events.get(name);
		if (list != null) {
			list.each(function(i, v) {
				if (v == fn) {
					list.remove(i);
					return false;
				}
			});
		}
	},
	fire : function(name) {
		var list = this.events.get(name);
		if (list == null) { return; }
		var args = Array.prototype.slice.call(arguments, 1);
		list.each(function(i, fn) {
			return fn.apply(null, args);
		});
	}
});

S.Class('S.Template', {
	init : function() {
		this.template = Array.prototype.join.call(arguments,'');
	},
	apply : function(conf) {
		return this.template.replace(/@\{(.*?)\}/g, function(g1, key) {
			return  conf[key]!=null?conf[key] : '';
		});
	},
	toString:function(){
		return this.template;
	}
});

/** ************************************************************* */
// Components
/** ************************************************************* */
// drag
S.Class('S.Dragable', {
	init : function(conf) {
		this.conf = conf || {};
		this.$el = $(conf.el);
		
		var handle = conf.handle || conf.el;
		this.$handle = $(handle); 
		this.data = {};
		this.enabled=true;
		
		this.events = new S.Event();
		if (S.isFunction(this.conf.moving)) {
			this.events.add('moving', this.conf.moving.bind(this));
		}
		if (S.isFunction(this.conf.start)) {
			this.events.add('start', this.conf.start.bind(this));
		}
		if (S.isFunction(this.conf.drop)) {
			this.events.add('drop', this.conf.drop.bind(this));
		}

		var mousemove = function(e) {
			e = e || event;
			S.stopEvent(e);

			var left = e.clientX - this.data.mouseX + this.data.x;
			var top = e.clientY - this.data.mouseY + this.data.y;

			this.setPos({left:left,top:top});
		}.bind(this);

		var selectstart = document.onselectstart;
		var moz_select = $(document.body).css('-moz-user-select');
		var mouseup = function(e) {

			document.onselectstart = selectstart;
			$(document.body).css('-moz-user-select', moz_select);
			S.delEvent(document, 'mousemove', mousemove);
			S.delEvent(document, 'mouseup', mouseup);
			this.events.fire('drop', {
				x : e.clientX,
				y : e.clientY
			});
		}.bind(this);

		var mousedown = function(e) {
			if(!this.enabled){return;}
			
			e = e || event;
			S.stopEvent(e);
			var offset = this.$el.position();
			
			this.data.x = offset.left;
			this.data.y = offset.top;
			
			this.data.mouseX = e.clientX;
			this.data.mouseY = e.clientY;

			document.onselectstart = function() {return false;};
			$(document.body).css('-moz-user-select', 'none');
			S.addEvent(document, 'mousemove', mousemove);
			S.addEvent(document, 'mouseup', mouseup);

			this.events.fire('start');
		}.bind(this);

		S.addEvent(this.$handle.get(0), 'mousedown', mousedown);
	},
	setPos : function(data) {
		
		if (S.isFunction(this.conf.position)) {
			data = this.conf.position.call(this, data);
		}

		var region = this.conf.region;
		if (region) {
			if (region.left != null && data.left < region.left) { 
				data.left = region.left;
			}else {
				var maxwidth = region.right;
				if (maxwidth == '100%') {
					maxwidth = this.$handle.parent().width();
				}
				if (maxwidth != null && data.left + this.$handle.width() > maxwidth) { 
					data.left = maxwidth-this.$handle.width();
				}
			}
			if (region.top != null && data.top < region.top) {
				data.top = region.top;
			}else{
				
				
				var maxheight = region.bottom;
				if (maxheight == '100%') {
					maxheight = this.$handle.parent().height();
				}
				if (maxheight != null && data.top + this.$handle.height() > maxheight) {
					data.top = maxheight-this.$handle.height();
				}
			}
		}

		var pos = {
			left : Math.floor(data.left),
			top : Math.floor(data.top)
		};
		
		if (this.conf.axis == 'x') {
			delete pos.top;
		} else if (this.conf.axis == 'y') {
			delete pos.left;
		}
		this.$el.css(pos);
		this.events.fire('moving', pos);
	},
	getPos:function(){
		return this.$el.position();
	},
	enable:function(enabled){
		this.enabled=enabled;
	}
});

/**
 * Loading
 */
S.Class('S.Loading', {
	init : function(msg, parent) {
		var parent = parent || document.body;
		this.guid = S.guid();

		this.msg = msg;
		var html = S.Loading.template.apply({
			id : this.guid
		});
		this.$dom = $(html);
		this.$dom.offset({
			top : 10,
			left : 10
		});

		if (parent != document.body) {
			var position = $(parent).css('position');
			if (position != 'absolute' && position != 'relative') {
				$(parent).css('position', 'relative');
			}
		}
		this.hide();
		this.$dom.appendTo(parent);
	},
	show : function() {
		var idx = 0;
		var showText = function() {
			var msg = S.Loading.icons[idx++ % S.Loading.icons.length] + ' ' + this.msg;
			this.$dom.html(msg);
		}.bind(this);
		showText();
		this.$dom.fadeIn();
		this.timer = setInterval(showText, 500);
	},
	text : function(str) {
		clearInterval(this.timer);
		this.$dom.html(str);
	},
	hide : function() {
		clearInterval(this.timer);
		this.$dom.hide();
		this.timer = null;

	},
	remove : function() {
		this.hide();
		this.$dom.remove();
	}
}, {
	template : new S.Template('<div class="s-loading" id="@{id}"></div>'),
	icons : [ '∵', '∴', '∷' ]
});

/**
 * Scrollable
 */
S.Class('S.Scrollable', {
	init : function(conf) {
		this.$el = $(conf.el);
		this.axis = conf.axis;

		var pos = this.$el.css('position');
		if (pos != 'relative' && pos != 'absolute') {this.$el.css('position', 'relative');}

		this.events = new S.Event();

		var containerWidth = this.$el.width(), containerHeight = this.$el.height(),barxWidth,barxHandleWidth,baryHeight,baryHandleHeight, me = this;
		
		if (this.axis == 'x' || this.axis == 'xy') {
			
			var dhxId = S.guid();
			var barxId = S.guid();
			$(S.Scrollable.template.apply({
				axis : 'x',
				barid : barxId,
				id : dhxId
			})).appendTo(this.$el);
			
			this.$el.hover(function() {
				
				if(me.$el.prop('scrollWidth')>me.$el.width()){
					$('#' + barxId).fadeIn();
				}
			}, function() {
				$('#' + barxId).hide();
			});

			barxWidth  = $('#' + barxId).width();
			barxHandleWidth=barxWidth*containerWidth/me.$el.prop('scrollWidth');
			$('#' + dhxId).width(barxHandleWidth);

			this.xDraggable= new S.Dragable({
				el : S.E(dhxId),
				axis : 'x',
				region : {
					left : 0,
					right : '100%'
				},
				moving : function(data) {
					
					var left = data.left;
					if (isNaN(left) ||  left < 0 ){
						left = 0;
					}else if(left + barxHandleWidth > barxWidth){
						left = barxWidth -barxHandleWidth;
					}
					var t = left * (me.$el.prop('scrollWidth') - containerWidth) / (barxWidth - barxHandleWidth);
					t = Math.floor(t);
					$('#' + barxId).css('left', t);
					me.$el.scrollLeft(t);
					me.events.fire('scrollX', t);
				}
			});

			this.events.add('scrollY', function(scrollY) {
				$('#' + barxId).css({top : scrollY + containerHeight - 13});
			}.bind(this));
			
			this.events.add('resize',function(){
				barxWidth  = $('#' + barxId).width();
				barxHandleWidth=barxWidth*containerWidth/me.$el.prop('scrollWidth');
				$('#' + dhxId).width(barxHandleWidth);
			});
		}

		if (this.axis == 'y' || this.axis == 'xy') {
			var dhyId = S.guid();
			var baryId = S.guid();
			$(S.Scrollable.template.apply({
				axis : 'y',
				barid : baryId,
				id : dhyId
			})).appendTo(this.$el);
			
			this.$el.hover(function() {
				if(me.$el.prop('scrollHeight')>me.$el.height()){
					$('#' + baryId).fadeIn();
				}
			}, function() {
				$('#' + baryId).fadeOut();
			});

			baryHeight = $('#' + baryId).height();
			baryHandleHeight=baryHeight*containerHeight/me.$el.prop('scrollHeight');
			$('#' + dhyId).height(baryHandleHeight);
			
			this.yDraggable = new S.Dragable({
				el : S.E(dhyId),
				axis : 'y',
				region : {
					top : 0,
					bottom : '100%'
				},
				moving : function(data) {
					var top =  data.top;
					if (isNaN(top) ||  top < 0 ){
						top = 0;
					}else if(top + baryHandleHeight > baryHeight){
						top = baryHeight -baryHandleHeight;
					} 
					var t = top * (me.$el.prop('scrollHeight') - containerHeight ) / (baryHeight-baryHandleHeight);
					t = Math.floor(t);
					$('#' + baryId).css('top', t);
					me.$el.scrollTop(t);
					me.events.fire('scrollY', t);
				}
			});
			this.events.add('scrollX', function(scrollX) {
				$('#' + baryId).css({left : scrollX + containerWidth-13});
			}.bind(this));
			
			this.events.add('resize',function(){
				baryHeight = $('#' + baryId).height();
				baryHandleHeight=baryHeight*containerHeight/me.$el.prop('scrollHeight');
				$('#' + dhyId).height(baryHandleHeight);
			});
		}
		
		this.events.add('resize',function(){
			containerWidth = me.$el.width(), containerHeight = me.$el.height();
		});
		
		this.$el.resize(function(){
			me.events.fire('resize');
		});
		
		S.addEvent(this.$el.get(0), 'mousewheel',function(d){
			if(this.yDraggable){
				if(me.$el.prop('scrollHeight')>me.$el.height()){
					var percent = this.yDraggable.getPos().top+ this.yDraggable.$el.height()*this.$el.prop('scrollHeight')/this.$el.height()*0.05*-d;
					this.yDraggable.setPos({top:percent});
				}
			}else if(this.xDraggable){
				if(me.$el.prop('scrollWidth')>me.$el.width()){
					var percent = this.xDraggable.getPos().left+ this.xDraggable.$el.width()*this.$el.prop('scrollWidth')/this.$el.width()*0.05*-d;
					this.xDraggable.setPos({left:percent});
				}
			}
		}.bind(this));
		
	},
	resize:function(){
		this.events.fire('resize');
	}
}, {
	template : new S.Template('<div class="s-scrollbar-@{axis}" id="@{barid}"><div class="s-scrollbar-handle-@{axis}" id="@{id}"></div></div>')
});

/*
 * Z-Index
 */
S.Class('S.Zindex',{
	init:function(){
		
		var zindex = S.Zindex.stack.get(S.Zindex.stack.size()-1);
		var index = S.Zindex.initIndex;
		if(zindex!=null){
			index = zindex.index()+5;
		}
		this.zindex = index;
		S.Zindex.stack.add(this);
	},
	index:function(){
		return this.zindex;
	},
	remove:function(){
		S.Zindex.stack.remove(this);
	}
},{
	stack:new S.List(),
	initIndex:999
});


/**
 * S.Cover
 */
S.Class('S.Cover', {
	init : function() {
		this.zIndex=  new S.Zindex();
		var guid = S.guid();
		
		var html = S.Cover.template.apply({
			id:guid,
			zindex:this.zIndex.index()
		});
		this.$el =$(html).prependTo(document.body);
	},
	show:function(){
		this.$el.show();
	},
	hide:function(){
		this.$el.hide();
	},
	remove:function(){
		this.$el.remove();
		this.zIndex.remove();
	}
}, {
	template : new S.Template('<div id="@{id}" style="position:fixed;width:100%;height:100%;z-index:@{zindex};background:#efefef;opacity:.6;display:none;"></div>')
});
/**
 * S.PageLoading
 */
S.Class('S.PageLoading', {
	init : function(conf) {
		this.conf = conf ||{
			modal:true
		};
		if(this.conf.modal){
			this.cover = new S.Cover();
		}
		var guid =  S.guid();
		this.zIndex=  new S.Zindex();
		var html = S.PageLoading.template.apply({
			id:guid,
			zindex:this.zIndex.index(),
			text:this.conf.text || '正在加载'
		});
		this.$el = $(html).appendTo(document.body);
	},
	show:function(){
		this.cover && this.cover.show();
		this.$el.show();
	},
	hide:function(){
		this.$el.hide();
		this.cover.hide();
	},
	remove:function(){
		this.$el.remove();
		this.cover && this.cover.remove();
	}
}, {
	template : new S.Template('<div class="s-paging-loading-container" id="@{id}" style="z-index:@{zindex}"><span class="s-paging-loading" class="pageload-overlay"></span>@{text}</div>')
});
/**
 * S.Info
 */
S.Class('S.Info', {
	init : function(conf) {
		this.conf = conf ||{};
		this.zIndex = new S.Zindex();
		var html = S.Info.template.apply(this.conf);
		this.$el = $(html).appendTo(window.top.document.body);
		this.$el.css('zIndex',this.zIndex.index());
	},
	show:function(){
		this.$el.show();
		(function() {
			var me = this;
			this.$el.animate({
				top:10,
				opacity:0
			},300,'linear',function(){
				me.$el.remove();
			});
			this.zIndex.remove();
		}).defer(this,1000);
	}
}, {
	template : new S.Template('<div class="s-infobox @{type}">@{text}</div>	')
});
/*S.MessageBox*/
S.Class('S.MessageBox', {
	init : function() {
		var html = S.MessageBox.template.toString();
		this.$container = $(html).appendTo(window.top.document.body);
		this.$mboxs = $('#s-mboxs');
		this.infos = new S.List();
		this.events = new S.Event();
		
		var idPre = S.guid();
		var idNext = S.guid();
		var idPager =S.guid();
		var idclear = S.guid();
		var pagerShow = false;
		this.currentPage=0;
		var pageSize = 3;
		
		var pageCount=1;
		this.events.add('messagenum:change',function(){
			var num = this.infos.size();
			pageCount = num%pageSize==0?num/pageSize:num/pageSize+1;
			
			this.$pager && $('#'+idPager).text(num+'条消息');;
			if(num>0){
				this.$container.show();
			}else{
				this.$container.hide();
			}
			
			//Pager Here
			if(num>pageSize){
				if(this.$pager== null){
					var html = S.MessageBox.pagerTemplate.apply({
						idpre:idPre,
						idnext:idNext,
						idpager:idPager,
						idclear:idclear,
						num:num
					});
					this.$pager = $(html).appendTo(this.$container);
					
					$('#'+idPre).click(function() {
						if(this.currentPage==0){return;}
						this.currentPage--;
						this.refresh();
					}.bind(this));
					
					$('#'+idNext).click(function() {
						var maxpage = this.infos.size()%pageSize==0?this.infos.size()/pageSize:this.infos.size()/pageSize+1;
						maxpage = parseInt(maxpage);
						if(this.currentPage>=maxpage-1){return;}
						this.currentPage++;
						this.refresh();
					}.bind(this));
					
					$('#'+idclear).click(function(){
						this.clear();
					}.bind(this));
					
					pagerShow = true;
				}
				if(!pagerShow){
					this.$pager.slideDown();
				}
			}else{
				this.$pager && this.$pager.slideUp();
				pagerShow = false;
			}
			
			if(this.currentPage>=pageCount){
				this.currentPage=pageCount-1;
			}
			this.refresh();
		}.bind(this));
	},
	clear:function(){
		this.infos.clear();
		this.$mboxs.empty();
		this.events.fire('messagenum:change');
	},
	refresh:function(){
		var start = this.currentPage*3;
		this.$mboxs.find('.s-mbox').hide();
		
		var max = start+2;
		this.infos.each(function(i,$e) {
			if(i>=start && i <= max){
				$e.show();
			}else{
				
			}
		});
	},
	addMessage:function(title,message,type){
		var type = type || 'primary';
		var id = S.guid();
		var html = S.MessageBox.mboxTemplate.apply({
			title:title,
			message:message,
			type:type,
			bordercolor:S.MessageBox.typeColor[type],
			id:id
		});
		var $e = $(html).appendTo(this.$mboxs);
		this.infos.add($e);
		var me = this;
		$('.s-mbox-close',$e).click(function(e) {
			$e.animate({
				left:800,
				height:0,
				width:0,
				marginBottom:0
			},800,function(){
				$e.remove();
				me.events.fire('messagenum:change');
			});
			me.infos.remove($e);
		});
		me.events.fire('messagenum:change');
	}
}, {
	template : new S.Template('<div class="s-mbox-container"><div id="s-mboxs"></div></div>'),
	mboxTemplate:new S.Template('<div class="s-mbox"  style="border-color:@{bordercolor}"> <div class="s-mbox-head @{type}">@{title} <a class="s-mbox-close" id="@{id}">×</a>	</div> <div class="s-mbox-content"> @{message} </div> </div>'),
	pagerTemplate:new S.Template('<div class="s-mbox-pager"><a class="s-mbox-btn"  id="@{idpre}">&lt;</a><a class="s-mbox-btn"  id="@{idnext}">&gt;</a><span id="@{idpager}">@{num}条消息</span> <a class="s-mbox-btn"  id="@{idclear}">全部清除</a></div>'),
	typeColor:{
		success:'#419641',
		danger:'#c12e2a',
		info:'#2aabd2',
		primary:'#2d6ca2',
		warn:'#eb9316'
	}
});

/*S.Slider*/
S.Class('S.Slider',{
	init:function(conf){
		this.axis = conf.axis;
		this.val = conf.val || 0;
		var el = conf.el;
		this.events = new S.Event();
		this.enabled = true;
		
		if(el!=null){
			if(S.isString(el)){
				this.$el = $('#'+el);
			}else{
				this.$el = $(el);
			}
		}else if(conf.container){
			var html = S.Slider.template.apply({
				axis:this.axis
			});
			this.$el = $(html).appendTo($(conf.container));
		}
		
		this.$range = this.$el.children('.s-slider-range');
		this.$bar = this.$el.children('.s-slider-bar');
		
		var me = this;
		if(this.axis == 'x'){
			var total = this.$el.width()-this.$bar.width()-2;
			this.total = parseFloat(total,10);
			this.draggable =  new S.Dragable({
				axis:this.axis,
				el:this.$bar.get(0),
				region:{
					left:0,
					right:'100%'
				},
				moving:function(pos){
					var percent = parseInt(pos.left/total *100);
					me.val = percent;
					me.events.fire('val:change',percent);
				},
				drop:function(){
					me.events.fire('drop');
				}
			});
		}else if(this.axis == 'y'){
			 var total = this.$el.height()-this.$bar.height()-2;
			 this.total  = parseInt(total);
			 this.draggable = new S.Dragable({
				axis:this.axis,
				el:this.$bar.get(0),
				region:{
					top:0,
					bottom:'100%'
				},
				moving:function(pos){
					var percent = parseInt(pos.top/total *100);
					me.val = percent;
					me.events.fire('val:change',percent);
				},
				drop:function(){
					me.events.fire('drop');
				}
			});
		}else{return;}
		
		this.events.add('val:change',function(val){
			var position = this.$bar.position();
			if(!this.enabled){return;}
			if(this.$range){
				if(this.axis == 'x'){
					this.$range.css('width',position.left+5);
				}else if(this.axis == 'y'){
					this.$range.css('height',position.top+5);
				}
			}
			S.isFunction(conf.change) && conf.change(val);
		}.bind(this));
		
		this.events.add('drop',function(){
			if(!this.enabled){return;}
			S.isFunction(conf.drop) && conf.drop(this.value());
		}.bind(this));
		
		this.set(this.val);
	},
	set:function(val){
		if(!this.enabled ||  val<0 || val>100){return;}
		this.val = val;
		
		var v =this.total*val/100;
		if(this.axis == 'x'){
			this.$bar.css('left',v);
		}else if(this.axis == 'y'){
			this.$bar.css('top',v);
		}
		this.events.fire('val:change',val);
	},
	value:function(){
		return this.val;
	},
	enable:function(enabled){
		this.enabled = enabled;
		this.draggable.enable(this.enabled);
	}
},{
	template:new S.Template('<div class="s-slider @{axis}"><div class="s-slider-range"></div><div class="s-slider-bar"></div></div>')
});

/**
 * S.ProgressBar
 */
S.Class('S.ProgressBar', {
	init : function(conf) {
		
		var el = conf.el;
		if(S.isString(el)){
			el= S.E(el);
		}
		var parent = el || document.body;
		var type = conf.type || 'primary';
		this.text =conf.text; 
		this.change = conf.change;
		this.val = conf.val || 0;
		
		if(parent!=null && $(parent).hasClass('s-progressbar')){
			this.$el = $(parent);
		}else{
			var html =  S.ProgressBar.template.apply({
				type:type,
			});
			this.$el = $(html).appendTo(parent);
		}
		this.$bar = this.$el.children('div');
		this.set(this.val);
	},
	set:function(percent){
		percent = parseInt(percent);
		if(percent<0 || percent>100 ){return;}
		this.$bar.css('width',percent+ '%');
		if(!this.text){
			this.$bar.text(percent+ '%');
		}
		this.val = percent;
		S.isFunction(this.change) && this.change.bind(this)(this.val);
	},
	value:function(){
		return this.val;
	}
}, {
	template : new S.Template('<div class="s-progressbar"><div class="s-bar @{type}">@{text}</div></div>')
});
S.Class('S.Dialog', {
	init : function(conf) {
		
		this.conf = S.fixConfig(conf, {
			modal:false,
			title:'来自网页的消息',
			content:'升职加薪当上总经理迎取白富美走向人生的巅峰',
			buttons:{
				'确认':function(){
					this.hide();
				},
				'取消':function(){
					this.hide();
				}
			},
			maxable:true,
			width:350,
			theme:'primary'
		});
		
		this.events = new S.Event();
		var me = this;
		
		var iddlg = S.guid(),idtitle=S.guid(),idclose=S.guid(),idmax=S.guid(),idfooterctrls=S.guid(),idcontent=S.guid();
		var content = this.conf.content;
		var html = S.Dialog.template.apply({
			iddlg:iddlg,
			idtitle:idtitle,
			idclose:idclose,
			idmax:idmax,
			idfooterctrls:idfooterctrls,
			title:this.conf.title,
			idcontent:idcontent,
			theme:this.conf.theme
		});
		this.$el = $(html).hide().appendTo(document.body);
		
		this.$content = $('#'+idcontent);
		
		if(this.conf.url){
			this.$content.load(this.conf.url,function(){
				me.events.fire('dialog:loaded');
			});
		}else{
			this.$content.html(content);
			me.events.fire('dialog:loaded');
		}
		
		this.events.add('dialog:loaded',function(){
			if(this.$content.find('form').size()>0){
				this.form = new S.Form(this.$content.find('form').get(0));
			}
		}.bind(this));
		
		if(this.$el.height()<200){
			this.$el.height(200);
		}
		if(this.conf.modal){
			this.cover = new S.Cover();
		}
		this.zIndex = new S.Zindex();
		this.$el.css({
			zIndex:this.zIndex.index(),
			width:this.conf.width,
			height:this.conf.height,
			left:($(document.body).width()-this.conf.width)/2
		});
		
		
		var $footerCtrls = $('#'+idfooterctrls,this.$el);
		S.each(this.conf.buttons,function(name,fn){
			var $btn = $('<a class="primary"></a>').text(name).appendTo($footerCtrls);
			if(fn){
				fn = fn.bind(me);
			}else{
				fn = me.hide;
			}
			$btn.click(fn);
		});
		
		$('#'+idclose).click(this.hide.bind(this));
		var maxed = false,width,height,pos;
		
		$('#'+idmax).click(function() {
			if(!maxed){
				width = this.$el.width();
				height = this.$el.height();
				pos = this.$el.position();
				this.$el.css({
					height:'100%',
					width:'100%',
					left:0,
					top:0
				});
				maxed = true;
				$('#'+idmax).text('＝');
				this.dragable.enable(false);
			}else{
				this.$el.css({
					height:height,
					width:width,
					left:pos.left,
					top:pos.top
				});
				$('#'+idmax).text('□');
				maxed = false;
				this.dragable.enable(true);
			}
			this.events.fire('size:change');
		}.bind(this));
		
		this.events.add('size:change',this.onresize);
		this.dragable= new S.Dragable({
			region:{
				left:0,
				top:0
			},
			el:S.E(iddlg),
			handle:S.E(idtitle)
		});
	},
	show:function(){
		this.cover && this.cover.show();
		this.$el.show();
	},
	hide:function(){
		this.$el.hide();
		this.cover && this.cover.hide();
	},
	remove:function(){
		this.hide();
		this.$el.hide();
		this.cover && this.cover.remove();
		S.delEvent(window,'resize',this.onresize);
	},
	resize:function(width,height){
		if(height<150 || width<200){return;}
		this.$el.css({
			height:height,
			width:width,
		});
		this.events.fire('size:change');
	}
}, {
	template : new S.Template('<div class="s-dlg @{theme}" id="@{iddlg}">',
			'	<div class="s-dlg-wrap">',
			'			<div class="s-dlg-head">',
			'				<div class="s-dlg-title" id="@{idtitle}">@{title}</div>',
			'					<div  class="s-dlg-ctrls"> ',
			'						<a id="@{idclose}">×</a>',
			'						<a id="@{idmax}">□</a>',
			'					</div>',
			'			</div>',
			'			<div class="s-dlg-main">',
			'				<div class="s-dlg-content" style="overflow:hidden;" id="@{idcontent}"></div>',
			'			</div>',
			'			<div class="s-dlg-footer">',
			'				<div id="@{idfooterctrls}">',
			'				</div>',
			'			</div>',
			'		 </div>',
			'	</div>',
			'</div>')
});

/*S.ContextMenu*/
S.Class('S.ContextMenu', {
	init : function(conf) {
		
		var data  =conf.items;
		this.$target = $(conf.el);
		this.$box = $(S.ContextMenu.template.toString()).appendTo(document.body);
		this.$menu = this.$box.children('ul');
		this.zIndex = new S.Zindex();
	
		this.$box.hide();
		this.itemFuns = {};
		this.menuItems = {};
		
		this.$box.css('zIndex',this.zIndex.index());
		
		var addItem = function(menu,name,data) {
			
			if(data == '|'){
				menu.append('<li class="s-contextmenu-item s-contextmenu-line"></li>');
				return;
			}
			var id = S.guid();
			var html = S.ContextMenu.itemTemplate.apply({
				id:id,
				text:name
			});
			var $item = $(html).appendTo(menu);
			if (S.isFunction(data)) {
				this.itemFuns[id]=data;
				this.menuItems[name] = {
						item:$item,
						id:id
				};
				return;
			}
			//subitems
			if(S.isObject(data)){
				$item.addClass('s-contextmenu-more');
				var $submenu = $('<ul class="s-contextmenu-menu"></ul>').appendTo($item);
				S.each(data,function(name,val){
					addItem($submenu,name,val);
				});
			}
		}.bind(this);
		S.each(data,function(name,val){
			addItem(this.$menu,name,val);
		}.bind(this));
		
		//bind events
		var me = this;
		this.$menu.delegate('.s-contextmenu-item a','click',function(){
			var $e = $(this);
			var text = $e.text();
			var menuItem =  me.menuItems[text];
			if(!menuItem || menuItem.disabled == true){
				return;
			}
			var id = $e.attr('id');
			me.itemFuns[id] && me.itemFuns[id]();
		});
		
		var docClick = function() {
			this.$box.hide();
			S.delEvent(document.body, 'click', docClick);
		}.bind(this);
		
		S.addEvent(this.$target.get(0), 'contextmenu', function(e) {
			var event = e || window.event;
			S.stopEvent(event);
			this.$box.css({
				left:event.pageX - 5,
				top:event.pageY - 5,
				display:'block'
			});
			S.addEvent(document.body, 'click',docClick);
		}.bind(this));
	},
	enable:function(names){
		if(S.isArray(names)){
			S.each(names, function(i,name) {
				this.enable(name);
			}.bind(this));
			return;
		}
		var menuItem  = this.menuItems[names];
		delete menuItem['disabled'];
		menuItem.item.children('a').removeClass('disable');
	},
	disable:function(names){
		if(S.isArray(names)){
			S.each(names, function(i,name) {
				this.disable(name);
			}.bind(this));
			return;
		}
		var menuItem  = this.menuItems[names];
		menuItem.item.children('a').addClass('disable');
		menuItem['disabled'] = true;
	},
	hide:function(){
		this.$box.hide();
	}
}, {
	template : new S.Template('<div class="s-contextmenu-menu-box"><ul class="s-contextmenu-menu"></ul></div>'),
	itemTemplate:new S.Template('<li class="s-contextmenu-item"><a id="@{id}">@{text}</a></li>'),
	line:new S.Template('<li class="s-contextmenu-item s-contextmenu-line"></li>')
});


/**
 * combox
 * editable combox
 * autocomplete combox
 * 
 */
S.Class('S.Select',{
	init:function(conf){
		var $select=  $(conf.el);
		var el = $select.get(0);
		if(!el ||  el.tagName.toLowerCase() !='select'){return;}
		
		this.options = new S.List();
		
		this.selectedValue = null;
		this.selectedIndex = -1;
		
		this.events = new S.Event();
		
		var me = this;
		$select.children().each(function(i) {
			var $option = $(this);
			var val = $option.val();
			var text = $option.text();
			var selected = $option.attr('selected') == 'selected';
			var option = {
					val:val,
					text:text,
					selected:selected
				};
			me.options.add(option);
			if(selected){
				me.selectedIndex = i;
				me.selectedValue = option;
			}
		});
		
		var name = $select.attr('name');
		this.disabled = $select.attr('disabled')=='disabled';
		var html = S.Select.template.apply({
			name:name
		});
		this.$el = $(html);
		this.$text = $('>input[type=text]',this.$el);
		this.$hidden = $('>input[type=hidden]',this.$el);
		this.$handle = $('.s-select-arr',this.$el);
		this.$text.width($select.width());
		
		$select.replaceWith(this.$el);
		this.$items = $('<div>').addClass('s-select-items').appendTo(this.$el);

		var scrollable;
		var show = false;
		this.$handle.click(function(){
			this.$items.find('li').show();
			if(!this.disabled){
				if(!show){
					this.$items.show();
					if(this.options.size()>6 && this.$items.is(":visible")){
						if(scrollable == null){
							scrollable = new S.Scrollable({
								el:this.$items,
								axis:'y'
							});
						}
					}
				}else{
					this.$items.hide();
				}
				show = !show;
			}
		}.bind(this));
		
		this.$items.delegate('li a','click',function(){
			if(me.disabled){return;}
			me.select($(this).parent().index());
			me.$handle.click();
		});
		
		this.events.add('value:change',function(){
			if(this.selectedValue!=null){
				this.$hidden.val(this.selectedValue.val);
				this.$text.val(this.selectedValue.text);
			}
			
			this.$items.find('.s-select-selected').removeClass('s-select-selected');
			if(this.selectedIndex>-1){
				this.$items.find('ul li').eq(this.selectedIndex).addClass('s-select-selected');
			}
			
			S.isFunction(conf.change) && conf.change(this);
		}.bind(this));
		
		this.events.add('item:change',function(){
			var html = [];
			this.options.each(function(i,o) {
				if(o.selected){
					html.push('<li><a class="s-select-selected">'+o.text+'</a></li>');
				}else{
					html.push('<li><a>'+o.text+'</a></li>');
				}
			});
			this.$items.find('ul').remove();
			this.$items.append('<ul>'+html.join('')+'</ul>');
			
		}.bind(this));
		
		if(conf.editable){
			this.$text.focus(function() {
				me.$items.show();
				show = true;
			});
			this.$text.keyup(function() {
				var text = this.$text.val();
				this.options.each(function(i,v){
					var $item = me.$items.find('ul li').eq(i);
					if(text == '' ||  v.text.indexOf(text)>-1){
						$item.show();
					}else{
						$item.hide();
					}
				});
				this.$hidden.val(this.$text.val());
				this.selectedIndex=-1;
				this.selectedValue=null;
			}.bind(this));
		}
		
		this.events.fire('item:change');
		if(this.selectedValue){
			this.$text.val(this.selectedValue.text);
		}
		if(!conf.editable){
			this.$text.attr('readonly','readonly');
		}
		if(this.disabled){
			this.$text.attr('readonly','readonly');
			this.$hidden.val('');
		}
	},
	value:function(){
		if(this.selectedValue){
			return this.selectedValue.val;
		}
		return null;
	},
	getSelectedIndex:function(){
		return this.selectedIndex;
	},
	getSelectedItem:function(){
		return this.selectedValue;
	},
	select:function(index){
		var option = this.options.get(index) || {
			val:'',
			text:'',
			selected:false
		};
		
		var old= this.options.get(this.selectedIndex);
		old && (old.selected = false);
		
		option.selected = true;
		this.selectedIndex = index;
		this.selectedValue = option;
		
		this.events.fire('value:change');
		
	},
	remove:function(index){
		
		this.options.remove(index);
		this.events.fire('item:change');
		if(index == this.selectedIndex){
			this.select(-1);
		}
		
	},
	empty:function(){
		this.$items.empty();
		this.options.clear();
		this.select(-1);
	},
	disable:function(flag){
		this.disabled = flag;
	}
},{
	template : new S.Template('<span class="s-select"><input  type="text"/><input type="hidden" name="@{name}"/><a class="s-select-arr"></a></span>')
});


/*S.AutoComplete*/
S.Class('S.AutoComplete',{
	init:function(conf){
		this.$text = $(conf.el);
		this.url = conf.url;
		this.events = new S.Event();
		var $span = this.$text.wrap('<span class="s-select"></span>').parent();
		var $items = $('<div class="s-select-items"><ul></ul></div>').appendTo($span);
		
		var timer;
		var me = this;
		this.$text.keyup(function() {
			timer = setTimeout(function() {
				clearTimeout(timer);
				$.post(me.url,{
					text:me.$text.val(),
					t:new Date().getTime()
				},function(data){
					me.data = eval(data);
					me.events.fire('data:change');
				},'text');
			}, 300);
		});
		
		this.events.add('data:change',function(){
			var val = this.$text.val();
			if(val == '' ||  this.data==null ||this.data.length==0){
				$items.hide();
				return;
			}
			var html = [];
			
			S.each(this.data, function(i,text) {
				html.push('<li><a>'+text+'</a></li>');
			},function(i,text){
				if(val =='' || text.indexOf(val)>-1){
					return true;
				}
			});
			if(html.length==0){
				$items.hide();
			}else{
				$items.find('ul').html(html.join(''));
				$items.show();
			}
		}.bind(this));
		
		$items.delegate('li a','click',function(){
			me.$text.val($(this).text());
			me.$text.focus();
			$items.hide();
		});
		me.$text.blur(function(){
			setTimeout(function() {
				$items.hide();
			}, 500);
			
		});
	}
});

/*S.HotKey*/
S.Class('S.HotKey', {
	init : function(el) {
		this.regkeys = new S.List();
		this.pressedkeys = new S.List();
		
		var el = el|| document.body;
		this.events  = new S.Event();
		
		$(el).keydown(function(e) {
			if(!this.pressedkeys.contains(e.keyCode)){
				this.pressedkeys.add(e.keyCode);
			}
		}.bind(this));
		
		$(el).keyup(function(e) {
			this.events.fire('keypress');
			this.pressedkeys.remove(e.keyCode);
		}.bind(this));
		
		this.events.add('keypress',function(){
			var me = this;
			if(this.regkeys.isEmpty()){return;}
			this.regkeys.each(function(i,reg){
			    reg.call && reg.call();
			},function(i,reg){
				return me.arrayEquals(reg.keys, me.pressedkeys.toArray());
			});
		}.bind(this));
	},
	registe:function(keys,fn){
		
		var kys = [];
		S.each(keys.toLowerCase().split('+'),function(i,v){
			if(S.HotKey.keys[v]){
				kys.push(S.HotKey.keys[v]);
			}
		});
		this.regkeys.add({
			keys:kys,
			call:fn
		});
	},
	unregiste:function(keys,fn){
		var me = this;
		this.regkeys.each(function(i,reg) {
			me.keys.remove(reg);
		},function(i,reg){
			return  me.arrayEquals(reg.keys, keys) && reg.call == fn;
		});
	},
	enable:function(enable){
		this.enabled = enable;
	},
	arrayEquals:function(arr1,arr2){
		if(arr1 == arr2){return true;}
		if(arr1.length !=arr2.length){return false;}
		for(var i in arr1){
			if(arr2.indexOf(arr1[i])==-1){
				return false;
			}
		}
		return true;
	}
}, {
	template : new S.Template(),
	keys:{
		"backspace":8,
		"tab":9,
		"enter":13,
		"shift":16,
		"ctrl":17,
		"alt":18,
		"capslock":20,
		"pause":19,
		"esc":27,
		"space":32,
		"pageup":33,
		"pagedown":34,
		"end":35,
		"home":36,
		
		"left":37,
		"up":38,
		"right":39,
		"down":40,
		
		"insert":45,
		"del":46,
		
		"0":48,
		"1":49,
		"2":50,
		"3":51,
		"4":52,
		"5":53,
		"6":54,
		"7":55,
		"8":56,
		"9":57,
		"-":173,
		"=":61,
		
		"f1":112,
		"f2":113,
		"f3":114,
		"f4":115,
		"f5":116,
		"f6":117,
		"f7":118,
		"f8":119,
		"f9":120,
		"f10":121,
		"f11":122,
		"f12":123,
		"numlock":144,
		"scroll":145,
		
		",":188,
		".":190,
		"/":191,
		"`":192,
		"[":219,
		"\\":220,
		"]":221,
		"'":222,
		";":59,
		
		'a':65,
		'b':66,
		'c':67,
		'd':68,
		'e':69,
		'f':70,
		'g':71,
		'h':72,
		'i':73,
		'j':74,
		'k':75,
		'l':76,
		'm':77,
		'n':78,
		'o':79,
		'p':80,
		'q':81,
		'r':82,
		's':83,
		't':84,
		'u':85,
		'v':86,
		'w':87,
		'x':88,
		'y':89,
		'z':90,
	}
});

S.Class('S.Pager',{
	init:function(page,max,size){
		this.max = max;
		this.page = page;
		this.size = size;
		if(this.max!=null){
			this.totalPage = this.max%size==0?parseInt(this.max/size):(parseInt(this.max/size)+1);
		}
		if(page==null){
			this.page = 1;
		}
		if(this.size==0){
			this.size = 20;
		}
	},
	hasPre:function(){
		return this.current>1;
	},
	hasNext:function(){
		if(!this.totalPage){return true;}
		return this.current>=this.pageNum;
	},
	getRange:function(){
		var end = this.page*this.size-1;
		if(this.max && end>max){
			end = this.max;
		}
		return {
			start:(this.page-1)*this.size,
			end:end
		};
	},
	setPage:function(page){
		if(page == null){return;}
		if(page<1){
			this.page = 1;
		}else if(this.totalPage && page>this.totalPage){
			this.page = this.totalPage;
		}else{
			this.page = page;
		}
	},
	setPageSize:function(size){
		this.init(this.page, this.max, size);
	},
	getPage:function(){
		return this.page;
	},
	getTotalPage:function(){
		return this.totalPage;
	},
	getMax:function(){
		return this.max;
	},
	getPageSize:function(){
		return this.size;
	}
});

/*S.PageBar*/
S.Class('S.PageBar', {
	init : function(conf) {

		var max = conf.max;
		var current = conf.current;
		var size = conf.size;
		var $el = $(conf.el);
		this.pager = new S.Pager(current,max,size);

		this.events = new S.Event();

		var idpagebar = S.guid(),idinfo = S.guid(),idselect = S.guid(),idcontrol = S.guid();

		var html = S.PageBar.template.apply({
			idpagebar:idpagebar,
			idinfo:idinfo,
			idselect:idselect,
			idcontrol:idcontrol,
			max:this.pager.getMax(),
			current:this.pager.getPage(),
			totalPage:this.pager.getTotalPage()
		});
		this.$el = $(html).appendTo($el);
		this.$info = this.$el.find('#'+idinfo);
		this.$controls = this.$el.find('.s-pagebar-contrls');
		
		var me = this;
		if(conf.slide){
			this.slider = new S.Slider({
				container:'#'+idcontrol,
				axis:'x',
				change:function(val){
					var page = Math.floor( me.pager.getTotalPage()*val/100);
					me.events.fire('paginginfo:change',page);
				},
				drop:function(val){
					me.pager.setPage(parseInt(me.pager.getTotalPage()*val/100));
					me.events.fire('page:change');
				}
			});
			this.slider.set(parseFloat(this.pager.getPage()/this.pager.getTotalPage() * 100));
			if(this.pager.getMax()==0){
				this.slider.enable(false);
			}
		}

		this.$select =  $('#'+idselect);
		this.$select.val(this.pager.getPageSize());

		this.$select.change(function() {
			var val = me.$select.val();
			me.pager.setPageSize(val);
			me.events.fire('page:change');
		});
		
		
		this.$controls.delegate('.refresh','click',function(){
			me.events.fire('page:change');
		});
		this.$controls.delegate('.resultset_first','click',function(){
			me.pager.setPage(1);
			me.events.fire('page:change');
		});
		this.$controls.delegate('.resultset_previous','click',function(){
			me.pager.setPage(me.pager.getPage()-1);
			me.events.fire('page:change');
		});
		this.$controls.delegate('.resultset_next','click',function(){
			me.pager.setPage(me.pager.getPage()+1);
			me.events.fire('page:change');
		});
		this.$controls.delegate('.resultset_last','click',function(){
			me.pager.setPage(me.pager.getTotalPage());
			me.events.fire('page:change');
		});
		
		me.events.add('paginginfo:change',function(page){
			if(page<1){
				page=1;
			}else if(page>this.pager.getTotalPage()){
				page = this.pager.getTotalPage();
			}
			this.$info.text('共'+this.pager.getMax()+'条记录 第'+page+'/'+this.pager.getTotalPage()+'页');
		}.bind(this));
		
		me.events.add('page:change',function(){
			if(this.slider){
				if(this.pager.getMax()==0){
					this.slider.set(0);
					this.slider.enable(false);
				}else{
					this.slider.enable(true);
					this.slider.set(parseFloat(this.pager.getPage()/this.pager.getTotalPage() * 100));
				}
			}
			conf.change && conf.change(this.pager.getPage());
		}.bind(this));
	},
	setPage:function(page){
		this.pager.setPage(page);
		this.events.fire('page:change');
	},
	setPagingInfo:function(current,max,size){
		this.pager =  new S.Pager(current,max,size);
		this.events.fire('paginginfo:change',current);
	},
	getPagingInfo:function(){
		return this.pager;
	},
	param:function(){
		return {page:this.pager.getPage(),size:this.pager.getPageSize()};
	}
}, {
	template : new S.Template(
			'<div class="s-pagerbar" id="@{idpagebar}">',
			'	<span id="@{idinfo}">共@{max}条记录 第@{current}/@{totalPage}页</span>',
			'	<span>每页<select id="@{idselect}"><option>10</option><option>20</option><option>50</option><option>100</option></select>行</span>',
			'	<div id="@{idcontrol}" class="s-pagebar-contrls"><span><a class="icons refresh"></a> <a class="icons resultset_first"></a> <a class="icons resultset_previous"></a> <a class="icons resultset_next"></a> <a class="icons resultset_last"></a></span> </div>',
			'</div>')
});

/*S.DatePicker*/
S.Class('S.DatePicker',{
	init:function(conf){
		conf = conf ||{};
		this.bindMap = new S.Map();
		this.currentId = null;
		this.currentDate =new Date();
		this.date = new Date();
		var idyear = S.guid(),idmonth = S.guid(),idtime=S.guid(),idbtntoday=S.guid(),idbtnyes=S.guid();
		var html = S.DatePicker.template.apply({
			idyear:idyear,
			idmonth:idmonth,
			year:this.currentDate.getFullYear(),
			month:this.currentDate.getMonth()+1,
			idtime:idtime,
			idbtnyes:idbtnyes,
			idbtntoday:idbtntoday
		});
		this.$el = $(html).appendTo(document.body);
		this.$dates = this.$el.find('.s-datepicker-data');
		this.$year = $('#'+idyear);
		this.$month = $('#'+idmonth);
		this.events = new S.Event();
		
		var getDaysOfMonth = function(year,month){
			month+=1;
			if(month==2){
				if((year/4!=0 && year/100==0) || year/4==0){
					return 29;
				}else{
					return 28;
				}
			}
			switch (month) {
			case 1:
			case 3:
			case 5:
			case 7:
			case 8:
			case 10:
			case 12:
				return 31;
			default:
				return 30;
			}
		};
		
		var me = this;
		$('#'+idbtnyes).click(function() {
			me.hide();
			me.date.setTime(me.currentDate.getTime());
			
			var conf = me.bindMap.get(me.currentId);
			
			if(!conf){return;}
			conf.date = me.date;
			conf && conf.ok && conf.ok(me.date);
		});
		$('#'+idbtntoday).click(function() {
			me.events.fire('focus:in');
			me.currentDate = new Date();
			me.events.fire('date:change');
		});
		this.$el.find('.s-datepicker-head .left-arr').click(function() {
			me.currentDate.setMonth(me.currentDate.getMonth()-1);
			me.events.fire('date:change');
		});
		this.$el.find('.s-datepicker-head .right-arr').click(function() {
			me.currentDate.setMonth(me.currentDate.getMonth()+1);
			me.events.fire('date:change');
		});
		
		this.events.add('focus:in',function(target){
			this.$yearpanel && target!=this.$yearpanel && me.$yearpanel.hide();
			this.$monthpanel && target!=this.$monthpanel  && me.$monthpanel.hide();
		}.bind(this));
		
		this.$year.click(function() {
			if(!me.$yearpanel){
				me.$yearpanel = $(S.DatePicker.yearTemplate.apply({})).appendTo(me.$el);
				var start = me.currentDate.getFullYear()-me.currentDate.getFullYear()%4;
				me.$yearpanel.delegate('ul li a','click',function() {
					var year = parseInt($(this).text());
					me.currentDate.setFullYear(year);
					start = me.currentDate.getFullYear()-me.currentDate.getFullYear()%4;
					me.events.fire('date:change');
					me.events.fire('year:change',start);
					me.$yearpanel.hide();
				});
				me.events.add('year:change',function(){
					var html =  [];
					for(var i=0;i<10;i++){
						html.push('<li><a>'+(start+i)+'</a></li>');
					}
					this.$yearpanel.find('ul').html(html.join(''));
					if(this.currentDate.getFullYear()>=start && this.currentDate.getFullYear()<start+10){
						this.$yearpanel.find('li').eq(this.currentDate.getFullYear()%4).addClass('selected');
					}
				}.bind(me));
				me.$yearpanel.find('.left-arr').click(function() {
					start-=10;
					me.events.fire('year:change');
					
				});
				me.$yearpanel.find('.right-arr').click(function() {
					start+=10;
					me.events.fire('year:change');
				});
				me.events.fire('year:change');
			}
			me.events.fire('focus:in',me.$yearpanel);
			me.$yearpanel.toggle();
		});
		
		this.$month.click(function() {
			if(!me.$monthpanel){
				me.$monthpanel = $(S.DatePicker.monthTemplate.apply({})).appendTo(me.$el);
				me.$monthpanel.delegate('ul li a','click',function() {
					var month = parseInt($(this).text());
					me.currentDate.setMonth(month-1);
					
					me.$monthpanel.find('.selected').removeClass('selected');
					$(this).addClass('selected');
					
					me.events.fire('date:change');
					me.$monthpanel.hide();
				});
				me.$monthpanel.find('li').eq(me.currentDate.getMonth()).children('a').addClass('selected');
			}
			me.events.fire('focus:in',me.$monthpanel);
			me.$monthpanel.toggle();
		});
		
		var idhours=S.guid(),idminutes=S.guid(),idsenconds=S.guid();
		html =  S.DatePicker.timeTemplate.apply({
			idhours:idhours,
			idminutes:idminutes,
			idsenconds:idsenconds
		});
		this.$times = $(html).appendTo(this.$el);
		
		this.$txttime = $('#'+idtime);
		this.$txttime.bind('focus click',function() {
			me.$times.show();
		});
		this.$txttime.blur(function() {
			me.$txttime.val(me.$txttime.val().trim());
		});
		
		this.$txttime.keypress(function(e) {
			var chars = String.fromCharCode(e.charCode);
			if(isNaN(chars)){return false;}
		});
		this.$txttime.change(function() {
			var val = me.$txttime.val();
			if(val== ''){val = '0:0:0';}
			if(!/(\d\d?):(\d\d?):(\d\d?)/.test(val)){
				me.$txttime.val('');
				return;
			}
			me.currentDate.setHours(RegExp.$1, RegExp.$2, RegExp.$3);
			me.events.fire('time:change');
		});
		
		var timeout;
		this.$times.mouseenter(function() {
			clearTimeout(timeout);
			var val = me.$txttime.val().trim();
			if(val!='' && me.$times.find('.selected').length==0){
				if(!/(\d\d:){2}(\d\d)/.test(val)){
					me.$txttime.val('');
				}else{
					me.events.fire('time:change');
				}
			}
			me.$times.show();
		});
		this.$times.mouseleave( function(){
			timeout = setTimeout(function(){
				me.$times.fadeOut();
			}, 400);
		});
		
		$('#'+idhours).delegate('li:gt(0) a','click',function(){
			me.currentDate.setHours($(this).text());
			me.events.fire('time:change');
		});
		$('#'+idminutes).delegate('li:gt(0) a','click',function(){
			me.currentDate.setMinutes($(this).text());
			me.events.fire('time:change');
		});
		$('#'+idsenconds).delegate('li:gt(0) a','click',function(){
			me.currentDate.setSeconds($(this).text());
			me.events.fire('time:change');
		});
		this.events.add('time:change',function(){
			this.$times.find('.selected').removeClass('selected');
			$('#'+idminutes).find('a').each(function(i,o) {
				if($(this).text() == me.currentDate.getMinutes()+''){
					$(this).addClass('selected');
				}
			});
			$('#'+idsenconds).find('a').each(function(i,o) {
				if($(this).text() == me.currentDate.getSeconds()+''){
					$(this).addClass('selected');
				}
			});
			
			$('#'+idhours).find('a').each(function(i,o) {
				if($(this).text() == me.currentDate.getHours()+''){
					$(this).addClass('selected');
				}
			});
			this.$txttime.val(padNumber(me.currentDate.getHours())+':'+padNumber(me.currentDate.getMinutes())+':'+padNumber(me.currentDate.getSeconds()));
		}.bind(this));
		
		function padNumber(number){
			if(number<10){
				return '0'+number;
			}
			return number;
		}
		
		this.events.add('date:change',function(){
			html = [];
			//上个月
			var d= new Date();
			d.setTime(this.currentDate.getTime());
			d.setDate(1);
			var firstDay = d.getDay();
			d.setMonth(this.currentDate.getMonth()-1);
			
			var days = getDaysOfMonth(d.getFullYear(),d.getMonth());
			
			for(var i=firstDay;i>0;i--){
				html.push('<li><a class="s-datepicker-out">'+(days-i)+'</a></li>');
			}
			//本月
			days = getDaysOfMonth(this.currentDate.getFullYear(),this.currentDate.getMonth());
			for(var i=0;i<days;i++){
				html.push('<li><a');
				if( i==this.currentDate.getDate()-1    ){
					html.push(' class="selected"');
				}
				html.push('>'+(i+1)+'</a></li>');
			}
			var x = 42-firstDay-days;
			//下个月
			for(var i=0;i<x;i++){
				html.push('<li><a class="s-datepicker-out">'+(i+1)+'</a></li>');
			}
			this.$dates.html(html.join(''));
			if(this.date.toDateString() == this.currentDate.toDateString()){
				this.$dates.find('li').eq(firstDay+this.currentDate.getDate()-1).addClass('selected');
			}
			
			this.$month.text(this.currentDate.getMonth()+1);
			this.$year.text(this.currentDate.getFullYear());
		}.bind(this));
		
		this.$dates.delegate('li a','click',function(){
			var $this = $(this);
			var date = parseInt($this.text());
			me.currentDate.setDate(date);
			var idx = $this.parent().index();
			
			if(idx<7&& date>20){
				me.currentDate.setMonth(me.currentDate.getMonth()-1);
				me.events.fire('date:change');
			}else if(date<15 && idx>30){
				me.currentDate.setMonth(me.currentDate.getMonth()+1);
				me.events.fire('date:change');
			}else{
				me.$dates.find('.selected').removeClass('selected');
				$(this).addClass('selected');
			}
			
			var conf = me.bindMap.get(me.currentId);
			conf && conf.click && conf.click(me.currentDate);
		});
		
		
		this.enter = false;
		this.$el.mouseenter(function(e) {
			me.enter = true;
		});
		
		this.$el.mouseleave(function(e) {
			me.enter =  false;
		});
		S.addEvent(document.body, 'click', function() {
			if(me.enter){return;}
			me.$el.hide();
		});
		
		this.events.fire('date:change');
	},
	bind:function(conf){
		var id = S.guid();
		var map = {
				el:conf.el,
				id:id,
				ok:conf.ok,
				click:conf.click,
				pattern:conf.pattern ||'yyyy-MM-dd'
			};
		map.showTime = map.pattern.indexOf(':')>0;
		this.bindMap.put(id,map);
	
		var $el = $(conf.el);
		
		if(!map.ok && $el.is('input[type=text]')){
			map.ok = function(date){
				$el.val(this.format(date,map.pattern));
			}.bind(this);
			var val  = $el.val();
			if(/(\d{4})-(\d\d?)-(\d\d?) (\d\d?):(\d\d?):(\d\d?)/.test(val) || /(\d{4})-(\d\d?)-(\d\d?)/.test(val) ){
				map.date = new Date(val.replaceAll('-','/'));
			}
		}
		var me = this;
		var pos = S.offset($el.get(0));
		var height = $el.height();
		var top = pos.top+height+2;
		$el.bind('click focus',function(){
			if($(window).height()<=pos.top+height+me.$el.height()){
				top=pos.top-me.$el.height()-2;
			}
			me.$el.css({
				left:pos.left,
				top:top
			});
			me.currentId = id;
			if(map.date){
				me.currentDate = new Date(map.date.getTime());
				me.date = new Date(map.date.getTime());
				me.events.fire('date:change');
				me.events.fire('time:change');
			}
			me.enter = true;
			me.show();
		});
		$el.bind('mouseout',function(){
			me.enter = false;
		});
	},
	show:function(){
		this.$el.show();
		var conf = this.bindMap.get(this.currentId);
		if(!conf || !conf.showTime){
			$('#'+idtime).parent().hide();
		}
	},
	hide:function(){
		this.$el.hide();
	},
	setDate:function(date){
		if(!date){return;}
		this.date.setTime(date.getTime());
		this.currentDate.setTime(date.getTime());
		this.events.fire('date:change');
	},
	getDate:function(){
		return this.date;
	},
	destory:function(){
		this.$el.remove();
	},
	format:function(date,pattern){
		var o = {
				'M+':date.getMonth()+1,
				'd+':date.getDate(),
				'h+':date.getHours(),
				'm+':date.getMinutes(),
				's+':date.getSeconds(),
				'q+':Math.floor((date.getMonth()+3)/3),
				'S':date.getMilliseconds()
		};
		if(/(y+)/.test(pattern)){
			pattern=pattern.replace(RegExp.$1,(date.getFullYear()+'').substring(4-RegExp.$1.length));
		}
		for(var k in o){
			if(new RegExp('('+k+')').test(pattern)){
				pattern = pattern.replace(RegExp.$1, (RegExp.$1.length==1)?o[k]:(('00'+o[k]).substr((''+o[k]).length)));
			}
		}
		return pattern;
	}
},{
	template:new S.Template(
			'<div class="s-datepicker">',
			'<div class="s-datepicker-head"><a class="left-arr"></a> <u id="@{idyear}">@{year}</u> 年 <u id="@{idmonth}">@{month}</u> 月 <a class="right-arr"></a></div>',
			'<div>',
			'	<ul class="s-datepicker-title">',
			'		<li class="weekend">日</li><li class="weekend">一</li><li class="weekend">二</li><li class="weekend">三</li><li class="weekend">四</li><li class="weekend">五</li><li class="weekend">六</li>',
			'	</ul>',
			'	<ul class="s-datepicker-data">',
			'	</ul>',
			'</div>',
			'<div class="s-datepicker-foot">',
			'	<span>时间<input type="text" name="time" class="s-datepicker-time" id="@{idtime}"/></span>',
			'	<a id="@{idbtntoday}">今天</a>',
			'	<a id="@{idbtnyes}">确定</a>',
			'</div>',
			'</div>'),
	timeTemplate:new S.Template(
			'<div class="s-datepicker-panel s-datepicker-times">',
			'<ul id="@{idhours}">',
			'	<li><b>时</b></li> <li><a>0</a></li> <li><a>1</a></li> <li><a>2</a></li> <li><a>3</a></li> <li><a>4</a></li> <li><a>5</a></li> <li><a>6</a></li> <li><a>7</a></li> <li><a>8</a></li> <li><a>9</a></li> <li><a>10</a></li> <li><a>11</a></li> <li><a>12</a></li> <li><a>13</a></li> <li><a>14</a></li> <li><a>15</a></li> <li><a>16</a></li> <li><a>17</a></li> <li><a>18</a></li> <li><a>19</a></li> <li><a>20</a></li> <li><a>21</a></li> <li><a>22</a></li><li><a>23</a></li>',
			'</ul>',	
			'<ul class="s-datepicker-minutes" id="@{idminutes}">',
			'	<li><b>分</b></li> <li><a>0</a></li> <li><a>5</a></li> <li><a>10</a></li> <li><a>15</a></li> <li><a>20</a></li> <li><a>25</a></li> <li><a>30</a></li> <li><a>35</a></li> <li><a>40</a></li> <li><a>45</a></li> <li><a>50</a></li> <li><a>55</a></li> <li><a>59</a></li>',
			'</ul>',
			'<ul class="s-datepicker-minutes" id="@{idsenconds}">',
			'	<li><b>秒</b></li> <li><a>0</a></li> <li><a>5</a></li> <li><a>10</a></li> <li><a>15</a></li> <li><a>20</a></li> <li><a>25</a></li> <li><a>30</a></li> <li><a>35</a></li> <li><a>40</a></li> <li><a>45</a></li> <li><a>50</a></li> <li><a>55</a></li> <li><a>59</a></li>',
			'</ul>',
			'</div>'),
	yearTemplate:new S.Template(
			'<div class="s-datepicker-panel s-datepicker-years">',
			'<a class="left-arr"></a>',
			'<ul>',
			'</ul>',
			'<a class="right-arr"></a>',
			'</div>'),
	monthTemplate:new S.Template(
			'<div class="s-datepicker-panel s-datepicker-months">',
			'<ul>',
			'	<li><a>1</a></li><li><a >2</a></li><li><a >3</a></li><li><a >4</a></li><li><a >5</a></li><li><a>6</a></li><li><a>7</a></li><li><a>8</a></li><li><a >9</a></li><li><a>10</a></li><li><a>11</a></li><li><a>12</a></li>',
			'</ul>',
			'</div>')
});

/*S.Tree*/
S.Class('S.Tree', {
	init : function(conf) {
		this.sid = S.guid();
		this.conf = S.fixConfig(conf, {
			checkbox:false,
			render:function(node){
				return node.text;
			}
		});
		this.events = new S.Event();
		var $container = $(conf.el);
		this.$el = $('<dl id="'+this.sid+'" class="s-tree">').appendTo($container);
		
		this.$el.delegate('dt a.tree-branch','click',function(){
			var $this = $(this);
			var dd  =$this.closest('dl').children('dd');
			if($this.is('.open')){
				$this.removeClass('open');
				dd.hide();
			}else{
				$this.addClass('open');
				dd.show();
			}
		});
		
		var me = this;
		this.$el.delegate('dt span','click',function(){
			var $this = $(this);
			var pid = $this.closest('dt').attr('id');
			var node = me.dataMap.get(pid);
			if(node == null){return;}
			var click = node.data.click || me.conf.click;
			click && click.defer(node,0);
		});
		
		this.$el.delegate('dt','click',function(){
			me.$el.find('.selected').removeClass('selected');
			$(this).addClass('selected');
		});
		//context menu
		if(this.conf.menus){
			var contextNode=null;
			function wrapItem (data,name,item){
				if(S.isObject(item)){
					S.each(item, function(key,iter) {
						wrapItem(item,key,iter);
					});
				}else if(S.isFunction(item)){
					data[name] = function() {
						item(contextNode);
					};
				}
			}
			S.each(this.conf.menus, function(key,fn){
				wrapItem(me.conf.menus,key,fn);
			});
			var contextMenu = new S.ContextMenu({
				el:this.$el,
				items:this.conf.menus
			});
			contextMenu.hide();
			
			this.$el.delegate('dt','contextmenu',function(){
				var id = $(this).attr('id');
				 contextNode = me.dataMap.get(id);
			});
		}
		//checkbox
		if(this.conf.checkbox){
			this.$el.delegate('a.checkbox','click',function(){
				var $this = $(this);
				var id = $this.closest('dt').attr('id');
				var node = me.dataMap.get(id);
				if(node == null){return;}
				node.check = !node.check;
				me.events.fire('node:click',node);
			});
			this.events.add('node:click',function(node){
				var sid = node.sid;
				var $node =  $('#'+sid);
				if(node.check){
					$node.closest('dl').find('.checkbox').addClass('checked');
				}else{
					$node.closest('dl').find('.checkbox').removeClass('checked');
				}
				var $dls= $node.parent('dl').parents('dl');
				$dls.each(function() {
					var $dl = $(this);
					var id = $dl.children('dt').attr('id');
					var n = me.dataMap.get(id);
					var $dd = $dl.children('dd');
					if($dd.find('.checkbox:not(.checked)').size() ==0){
						$dl.children('dt').children('.checkbox').removeClass('checkpart').addClass('checked');
						n &&(n.check  = true);
						n && me.events.fire('node:change',n);
					}else if ($dd.find('.checked').size() ==0){
						$dl.children('dt').children('.checkbox').removeClass('checkpart').removeClass('checked');
						n && (n.check  = false);
						n && me.events.fire('node:change',n);
					}else{
						$dl.children('dt').children('.checkbox').removeClass('checked').addClass('checkpart');
					}
				});
				me.events.fire('node:change',node);
			}.bind(this));
			
			this.events.add('node:change',function(node){
				this.conf.change && this.conf.change(node);
			}.bind(this));
			
			if(this.conf.ds){
				$.post(this.conf.ds,function(json){
					if(json.type == 'flat'){
						me.setFlatData(json.data);
					}else{
						me.setData(json.data);
					}
				});
			}
		}
	},
	/*立体的数据结构*/
	setData:function(data){
		this.dataMap = new S.Map();
		var me = this;
		var html = [];
		function add(node,last,pre) {
			var sid = S.guid();
			me.dataMap.put(sid,{
				sid:sid,
				data:node
			});
			html.push('<dl><dt id="'+sid+'">'+pre);
			if(node.children){
				html.push('<a class="tree-icons tree-branch open '+(last?'last':'')+' "></a>');
			}else{
				html.push('<a class="tree-icons tree-leaf '+(last?'last':'')+'"></a>');
			}
			if(me.conf.checkbox){
				html.push('<a class="tree-icons checkbox '+(node.check?'checked':'')+'"></a>');
			}
			if(node.icon){
				html.push('<a class="'+node.icon+'"></a>');
			}else if(node.children){
					html.push('<a class="tree-icons folder"></a>');
			}
			html.push('<span>'+(me.conf.render(node))+'</span></dt>');
			if(node.children  && node.children.length){
				html.push('<dd>');
				S.each(node.children, function(i,child) {
					add(child, i==node.children.length-1,pre+(last?'<a class="tree-icons"></a>':'<a class="tree-icons tree-v"></a>'));
				});
				html.push('</dd>');
			}
			html.push('</dl>');
		}
		S.each(data, function(i,node) {
			add(node,i==data.length-1,'');
		});
		this.$el.html(html.join(''));
	},
	/*扁平的数据结构*/
	setFlatData:function(data){
		var dm = new S.Map();
		S.each(data, function(i,d) {
			dm.put(d.id,d);
		});
		
		var fdata  = [];
		function format(data) {
			if(data.pid!=null){
				var d = dm.get(data.pid);
				if(d){
					if(!d.children){d.children=[];}
					 d.children.push(data);
				}
			}else{
				fdata.push(data);
			}
		}
		S.each(data, function(i,d) {
			format(d);
		});
		this.setData(fdata);
	},
	getSelectedItems:function(fmt){
		fmt = fmt || function(d) {return d;};
		var me = this;
		var result = [];
		if(this.conf.checkbox){
			this.$el.find('.checked').each(function() {
				var id = $(this).parent().attr('id');
				data = fmt(me.dataMap.get(id));
				result.push(data);
			});
		}else{
			this.$el.find('.selected').each(function() {
				var id = $(this).parent().attr('id');
				data = fmt(me.dataMap.get(id));
				result.push(data);
			});
		}
		return data;
	},
	hide:function(){
		this.$el.hide();
	},
	show:function(){
		this.$el.show();
	},
	expandAll:function(){
		this.$el.find('dd').show();
		this.$el.find('.tree-branch').addClass('open');
	},
	collapseAll:function(){
		this.$el.find('dd').hide();
		this.$el.find('.tree-branch').removeClass('open');
	}
});

/*S.Table*/
S.Class('S.Table', {
	init : function(conf) {
		
		this.conf = S.fixConfig(conf, {
			pager:false,
			scroll:false,
			clicks:{},
			renders:{}
		});
		
		var $table = $(conf.el);
		
		var form = $table.attr('form');
		if(form){
			this.$form = $('form[name='+form+']');
		}
		this.ds = $table.attr('ds');
		this.multiselect = $table.attr('multiselect') != 'false';
		var height = $table.height();
		
		this.data = [];
		this.sortMap = {};
		
		this.events = new S.Event();
		var idtable=S.guid(),idhead=S.guid(),idbody=S.guid(),idfooter=S.guid(),idcheckall=S.guid();
		this.$el = $(S.Table.template.apply({
			idtable:idtable,
			idhead:idhead,
			idbody:idbody,
			idfooter:idfooter
		}));
		this.$el.height(this.height);
		this.$spliter = this.$el.find('.spliter');
		this.$spliter.hide();
		this.loading = new S.Loading('正在加载....',this.$el);
		this.$thead = this.$el.find('.table_head');
		this.$tbody = this.$el.find('.table_body');
		this.$tfooter = this.$el.find('.footer');
		
		this.$tbody.height( this.conf.pager?height-60:height-30);
		/*find cols*/
		var columns = [];
		$table.find('tr:eq(0) td').each(function(i,td){
			var $td = $(this);
			var checkbox = $td.is('[checkall]');
			var seq = $td.is('[seq]');
			columns.push({
				sort:$td.attr('sort'),
				resize:$td.attr('resize')!='false',
				text: $td.html(),
				width:$td.attr('width'),
				checkbox:checkbox,
				seq:seq,
				col:$td.attr('col')
			});
		});
		
		var tableHead = [];
		tableHead.push('<table width="100%"><tr>');
		var checkIndex;
		S.each(columns, function(i,column) {
			tableHead.push('<td ');
			if(column.width){
				tableHead.push('width="'+column.width+'"');
			}
			tableHead.push('><div>');
			if(column.seq){
				tableHead.push('序号');
			}else if(column.checkbox){
				checkIndex = i;
				tableHead.push('<input type="checkbox" id="'+idcheckall+'"/>');
			}else{
				tableHead.push(column.text);
			}
			if(column.sort){
				tableHead.push('<span class="s-sort"></span>');
			}
			tableHead.push('</div></td>');
		});
		tableHead.push('</tr></table>');
		
		this.$thead.html(tableHead.join(''));
		
		var me = this;
		var resizeable = false;
		this.$thead.delegate('td','mousemove',function(e){
			var $this = $(this);
			var i = $this.parent().index();
			if(!columns[i].resize || i>=columns.length-1){return;}
			
			var width = $this.width();
			var left = $this.offset().left;
			if(width+left-e.pageX>2){	
				resizeable = false;
				me.$thead.css('cursor','default');
			}else{
				resizeable = true;
				me.$thead.css('cursor','col-resize');
			}
		});
		
		this.$thead.delegate('td div','mousedown',function(e){
			var $this = $(this);
			var i = $this.parent().index();
			if(!columns[i].resize || i>=columns.length-1){return;}
			
			var width = $this.parent().width();
			var left = S.offset($this.parent().get(0),me.$el.get(0)).left;
			
			if(width+left-e.pageX>2){return;}
			
			me.$spliter.css({left:width+left}).show();
			
			var x = e.pageX;
			var $next = $this.parent().next('td');
			var nw = $next.width();
			var disselect = function(e) {return false;};
			
			var tablewidth = $this.closest('table').width()-12;//去掉12看上去精度会更好一点。
			var toPercent = (width+nw)/tablewidth*100;
			var l;
			var mousemove=function (e){
				if((e.pageX-left<80) || (nw-e.pageX+x<80)){return;}
				me.$spliter.css({left:e.pageX-5});
				l = e.pageX;
			};
			var mouseup = function(e) {
				me.$spliter.hide();
				S.delEvent(document.body,'mousemove',mousemove);
				S.delEvent(document.body,'mouseup',mouseup);
				S.delEvent(me.$el.get(0), 'selectstart', disselect);
				me.$el.css('-webkit-user-select','');
				if(!l){return;}
				var wpercent = Math.floor(((width+l-x)/tablewidth*100));
				var npercent = Math.floor((toPercent-wpercent));
				
				$this.parent().attr('width',wpercent+'%');
				$next.attr('width',npercent+'%');
				
				columns[i].width = wpercent+'%';
				columns[i+1].width = npercent+'%';
					
				me.$tbody.find('tr:eq(0) td:eq('+i+')').attr('width',wpercent+'%');
				me.$tbody.find('tr:eq(0) td:eq('+(i+1)+')').attr('width',npercent+'%');
			};
			S.addEvent(document.body, 'mousemove', mousemove);
			S.addEvent(document.body,'mouseup',mouseup);
			S.addEvent(me.$el.get(0), 'selectstart', disselect);
			me.$el.css('-webkit-user-select','none');
		});
		this.$thead.find('#'+idcheckall).click(function() {
			if(!me.multiselect){return false;}
			var $this = $(this);
			me.$tbody.find('tr').each(function(i,row) {
				var $row = $(this);
				$row.removeClass('selected');
				if($this.attr('checked')){
					$row.find('td:eq('+checkIndex+') input[type=checkbox]').attr('checked','checked');
					$row.addClass('selected');
				}else{
					$row.find('td:eq('+checkIndex+') input[type=checkbox]').removeAttr('checked');
				}
			});
		});
		
		//排序
		this.$thead.delegate('.s-sort','click',function(){
			var $this = $(this);
			var order = '';
			if($this.is('.desc')){
				order = 'asc';
			}else{
				order = 'desc';
			}
			
			var colIdx = $this.closest('td').index();
			var column = columns[colIdx];
			
			me.events.fire('set:sortcol',column.sort,order);
			
			me.sort(order,column.sort);
		});
		
		this.events.add('set:sortcol',function(col,order){
			
			if(col == this.sortMap.col && order == this.sortMap.order){return;}
			
			this.$thead.find('.asc').removeClass('asc');
			this.$thead.find('.desc').removeClass('desc');
			
			var indx = -1;
			S.each(columns,function(i,column){
				if(column.sort == col){
					indx = i;
					return false;
				}
			});
			if(indx>-1){
				this.$thead.find('td:eq('+indx+') .s-sort').removeClass('asc').removeClass('desc').addClass(order);
			}
		}.bind(this));
		 
		//行选中
		this.$tbody.delegate('tr','click',function(){
			
			var $this = $(this);
			if(!me.multiselect ){
				
				me.$tbody.find('tr').each(function(){
					var $tr = $(this);
					$tr.children('td:eq('+checkIndex+') input[type=checkbox]').removeAttr('checked');
				});
				me.$tbody.find('tr').removeClass('selected');
			}
			var $checkbox = $this.find('input[type=checkbox]');
			
			if($this.is('.selected')){
				$checkbox.removeAttr('checked');
				$this.removeClass('selected');
			}else{
				$checkbox.attr('checked','checked');
				$this.addClass('selected');
			}
		});
		
		this.$tbody.delegate('td','click',function(e){
			
			var $this = $(this);
			var idx = $this.index();
			var column = columns[idx];
			if(!column || !column.col){return;}
			
			var rowIndex = $this.parent('tr').index();
			var row = me.data[rowIndex];
			if(!row){return;}
			
			var fn = me.conf.clicks[column.col];
			fn && fn.defer(document.body,1,row,columns[idx]);
		});
		this.$tbody.delegate('td','dbclick',function(e){
			
			var $this = $(this);
			var idx = $this.index();
			var column = columns[idx];
			if(!column || !column.col){return;}
			
			var rowIndex = $this.parent('tr').index();
			var row = me.data[rowIndex];
			if(!row){return;}
			
			var fn = me.conf.dbclicks[column.col];
			fn && fn.defer(document.body,1,row,columns[idx]);
		});
		
		//fill rows
		var tr1 = $table.find('tr:eq(1)').html();
		this.rowTemplate = new S.Template('<tr>',tr1,'</tr>');
		
		//pager and scrollable
		$table.replaceWith(this.$el);
		
		var tablewidth = this.$thead.find('table').width();
		this.$thead.find('td').each(function(i) {
			var $this = $(this);
			var width = columns[i].width;
			if(!width || width.indexOf('%')<1){
				width= Math.ceil(($this.width())/tablewidth*100)+'%';
				columns[i].width = width;
			}
			$this.attr('width',width);
		});
		
		if(this.conf.scroll){
			this.scrollable=new S.Scrollable({
				el:'#'+idbody,
				axis:'y'
			});	
		}
		if(this.conf.pager){
			this.pagebar = new S.PageBar({
				el:this.$tfooter,
				max:0,
				size:20,
				current:1,
				slide:true,
				change:function(val){
					me.events.fire('page:change',val);
				}
			});
		}
		
		//page chagne:update the pagerbar
		this.events.add('page:change',function(page){
			var param = this.pagebar.param();
			param.page = page;
			this.events.fire('load:data',param);
		}.bind(this));
		
		this.events.add('load:data',function(param){
			
			this.loading.show();
			param =  param || {};
			
			if(this.ds){
				if(this.$form){
					S.fixConfig(param, S.formData(this.$form),this.sortMap);
				}
				$.ajax(this.ds,{
					data:param,
					cache:false,
					type:'post',
					dataType:'json',
					success:function(text){
						var json = eval(text);
						me.setData(json);
					},
					complete:function(e){
						me.loading.hide.defer(me.loading,500);
					}
				});
			}else{
				var datas = [];
				if(conf.change){
					datas = conf.change(val);	
				}
				if(datas && datas.length>0){
					this.setData(datas);
				}
				this.loading.hide.defer(loading,500);
			}
		}.bind(this));
		
		//data change ,update the table data
		this.events.add('data:change',function(){
			var rows = [];
			S.each(this.data, function(i,d) {
				
				var row = {};
				S.each(d,function(k,v){
					row[k] = v;
				});
				
				if(row.seq == null){
					row.seq=  i+1;
				}
				if(me.conf.renders){
				 	S.each(me.conf.renders, function(col,render) {
				 		row[col] = render(d[col]);
					});
				}
				rows.push(me.rowTemplate.apply(row));
			});
			this.$tbody.children('table').remove();
			this.$tbody.append('<table width="100%">'+rows.join('')+'</table>');
			
			this.$tbody.find('tr:eq(0) td').each(function(i) {
				var width = columns[i].width || '';
				if(width){
					$(this).attr('width',width);
				}
			});
			if(this.conf.scroll){
				this.scrollable.resize();
			}
		}.bind(this));
		
		this.events.add('resize',function(){
			var height = this.$el.height();
			this.$tbody.height(this.conf.pager?height-60:height-30);
		});
		
		this.events.add('data:sort',function(order,col){
			
			this.sortMap.col = col;
			this.sortMap.order = order;
			
			if(this.conf.sort){
				this.conf.sort({
					page:this.pagebar.getPagingInfo().getPage(),
					col:col,
					order:order
				});
			}else if(this.ds){
				//服务器刷新
				this.events.fire('load:data');
			}else{
				this.data.sort(function(a,b){
					if(order == 'asc'){
						return a[col]>b[col];
					}else{
						return b[col]>a[col];
					}
				});
				this.events.fire('data:change');	
			}
		}.bind(this));

		//初始化数据
		if(this.ds){
			this.events.fire('page:change',1);
		}
	},
	setData:function(json){
		if(json.vos){
			this.data = json.vos;
		}
		if(json.paging && this.conf.pager){
			this.pagebar.setPagingInfo(json.paging.pageNumber,json.paging.maxRowNumber,json.paging.pageSize);
		}
		
		if(json.voMap.sortCol){
			this.events.fire('set:sortcol',json.voMap.sortCol,json.voMap.order);
		}
		this.events.fire('data:change');
	},
	sort:function(order,col){
		this.events.fire('data:sort',order,col);
	},
	show:function(){
		this.$el.show();
	},
	setPage:function(page){
		if(this.conf.pager){
			this.pagebar.setPage(page);
		}
	},
	hide:function(){
		this.$el.hide();
	},
	getCheckedRow:function(){
		
	},
	resize:function(){
		this.events.fire('resize');
	}
}, {
	template : new S.Template(
			'<div id="@{idtable}" class="s-table-container"><div style="position:relative;">',
			'	<div  class="table_head" id="@{idhead}" onselectstart="return false;"></div>',
			'	<div class="table_body" id="@{idbody}"></div>',
			'	<div class="footer" id="@{idfooter}"></div>',
			'	<div class="spliter"></div>',
			'</div></div>'
	)
});

/*Forms*/
S.Class('S.Form',{
	init:function(form){
		this.$form = $(form);
		var tips = new S.Tips();
		this.$form.find('[tips]').each(function(i,e) {
			tips.bind($(this));				
		});
		var me = this;
		this.$form.submit(function() {
			if(!me.validate()){
				new S.Info({text:'亲!表单填写有未完整的地方',type:'warn'}).show();
				return false;
			}
			return valide;
		});
		
		var $errmsg = $('<span id="xx" class="frmmsg warn"></span>').appendTo(this.$form);
		$errmsg.hide();
		this.$form.delegate('[check].error','focus',function(){
			var $this = $(this);
			var msg = $this.attr('errmsg');
			if(msg == null || msg.trim() == ''){return;}
			var pos = S.offset($this.get(0),me.$form.get(0));
			$errmsg.css({
				top:pos.top,
				left:pos.left+$this.width()+10
			}).html(msg).fadeIn();
			
			$this.blur(function(){
				$errmsg.hide();
			});
		});
		
		//data type
		var dateeles =  this.$form.find('[datetype]');
		if(dateeles.size()>0){
			var datePicker = new S.DatePicker();
			dateeles.each(function(i) {
				var $this = $(this);
				var pattern = $this.attr('datetype');
				datePicker.bind({
					el:this,
					pattern:pattern
				});
			});
		}
	},
	validate:function(){
		var valide = true;
		this.$form.find('[check]').each(function(i,e) {
			var $this = $(this);
			var type = $this.attr('check');
			var val = $this.val();
			var pattern;
			S.each(type.split(' '), function(i,t) {
				type = t;
				if(/^(\w+)\((.+?)\)$/.test(type)){
					type = RegExp.$1;
					fn = S.Form.validators[type];
					pattern = RegExp.$2;
				}
				var fn = S.Form.validators[type];
				$this.removeClass('error');
				if(fn){
					if( !fn(val,pattern)){
						$this.addClass('error');
						valide = false;
						return false;
					}
				}
			});
		});
		return valide;
	},
	param:function(){
		if(this.form){
			return $.param(this.$form.serialize());
		}else{
			return {};
		}
	},
	submit:function(){
		this.$form.submit();
	}
},{
	validators:{
		required:function(val,pattern ){
			return val.trim() != '';
		},
		email:function(val,pattern){
			return /^\w+@\w+\.\w+$/.test(val);
		},
		number:function(val){
			return val.trim()!='' && !isNaN(val);
		},
		ipv4:function(val){
			return /^(((25[0-5])|(2[0-4]\d)|(\d{1,2}))\.){3}((25[0-4])|(2[0-4]\d)|(\d{1,2}))$/.test(val);
		},
		length:function(val,len){
			return val.length==len;
		},
		digit:function(val){
			return /^\d+$/.test(val);
		},
		min:function(val,p){
			return val >= parseInt(p);
		},
		max:function(val,p){
			return val.trim()!='' && val <= parseInt(p);
		},
		range:function(val,pattern){
			var ps = pattern.split(',');
			return val.trim()!='' && val >= parseInt(ps[0]) && val <= parseInt(ps[1]) ;
		},
		minlen:function(val,len){
			return val.length >=len;
		},
		maxlen:function(val,len){
			return val.length>0 && val.length <=len;
		},
		rangelength:function(val,type){
			var lens = type.split(',');
			return val.length>=parseInt(lens[0]) && val.length < parseInt(lens[1]);
		},
		regex:function(val,regex){
			return val!='' && new RegExp(regex).test(val);
		},
		equalTo:function(val,other){
			return val == $(other).val();
		}
	}}
);

/*S.Tips*/
S.Class('S.Tips',{
	init:function(){
		this.$el = $(S.Tips.template.apply({
			text:''
		})).appendTo(document.body);
	},
	bind:function(el){
		var $el = $(el);
		var me = this;
		$el.hover(function(e) {
			var offset = S.offset(this);
			me.$el.html($el.attr('tips'));
			me.$el.css({
				left:offset.left+$el.width()+10,
				top:offset.top-5
			});
			me.$el.show();
		},function(e){
			me.$el.hide();
		});
	}
},{
	template:new S.Template('<span class="tips">@{text}</span>')
});

/*S.Tab*/
S.Class('S.Tab',{
	init:function(el){
		$(el).each(function() {
			var $this = $(this);
			var tabmap  =  new S.Map();
			var tabs = [];
			
			$this.next('.tabcontent').children().each(function(i) {
				var $tab = $(this);
				tabs.push($tab);
			});
			
			tabs.reverse();
			$this.children().each(function(i) {
				var $child = $(this);
				var url = $child.attr('url');
				var ref = $child.attr('ref');
				if((url == null || url.trim() == '') && (ref == null ||ref.trim() == '' ) && tabs.length>0){
					tabmap.put(i+'',tabs.pop());
				}
			});
			
			var $tabcontent = $this.next('.tabcontent');
			$this.children().click(function(){
				$tabcontent.children().hide();
				var _$this = $(this);
				var idx = $(this).index();
				$this.find('.current').removeClass('current');
				_$this.addClass('current');
				
				var url = _$this.attr('url');
				var ref = _$this.attr('ref');
				
				if(url && url.trim()!=''){
					var $tab = tabmap.get(url);
					if($tab){
						$tab.show();
					}else{
						$tab = $('<div>').appendTo($tabcontent);
						tabmap.put(url,$tab);
						$tab.load(url);
					}
				}else if(ref && ref.trim()!=''){
					var $tab = tabmap.get(ref);
					if(!$tab){
						$tab = $(ref);
						tabmap.put(ref,$tab);
					}
					$tab.show();
				}else{
					var $tab = tabmap.get(idx+'');
					$tab && $tab.show();
				}
			});
			if($this.find('.current').size() == 0){
				$this.children().eq(0).click();
			}
		});
	}
});